home *** CD-ROM | disk | FTP | other *** search
- unit HVHookDLL;
- { General DLL hooking utility
-
- Hook any implicitly imported DLL routine used by a loaded module.
-
- Written by Hallvard Vassbotn, hallvard@balder.no, September 1999
-
- Inspired by Matt Pietrek's article "Peering inside the PE: A Tour of the Win32
- Portable Executable File Format", available at:
-
- http://msdn.microsoft.com/library/techart/msdn_peeringpe.htm
-
- Quote: "Since the import address table is in a writeable section, it's
- relatively easy to intercept calls that an EXE or DLL makes to another DLL.
- Simply patch the appropriate import address table entry to point at the
- desired interception function. There's no need to modify any code in either
- the caller or callee images. What could be easier?"
- }
- interface
-
- function ReplaceImport(Base: Pointer; ModuleName: PChar; FromProc, ToProc: pointer): boolean;
- function HookImport(ModuleName, ImportName: PChar; HookProc: pointer; var DLLProc: pointer): boolean;
- function UnhookImport(ModuleName, ImportName: PChar; HookProc: pointer; var DLLProc: pointer): boolean;
-
- implementation
-
- uses
- Windows,
- SysUtils,
- HVPEUtils;
-
- type
- PWin95CallThunk = ^TWin95CallThunk;
- TWin95CallThunk = packed record
- PUSH: byte; // PUSH instruction opcode (=$68)
- Addr: pointer; // The actual address of the DLL routine
- JMP : byte; // JMP instruction opcode (=$E9)
- Rel : Integer; // Relative displacement (a Kernel32 address)
- end;
-
- function IsWin95CallThunk(Thunk: PWin95CallThunk): boolean;
- begin
- Result := (Thunk^.PUSH = $68) and (Thunk^.JMP = $E9);
- end;
-
- function ReplaceImport(Base: Pointer; ModuleName: PChar; FromProc, ToProc: pointer): boolean;
- var
- NtHeader : PImageNtHeaders;
- ImportDescriptor : PImageImportDescriptor;
- ImportEntry : PImageThunkData;
- CurrModuleName : PChar;
- IsThunked : Boolean;
- FromProcThunk : PWin95CallThunk;
- ImportThunk : PWin95CallThunk;
- FoundProc : boolean;
- begin
- // Assume failure
- Result := false;
-
- // Cache some Win95-specific knowledge about the FromProc
- // On Win95/98, the import entries and GetProcAddress of some system
- // module routines point to PUSH [ActualAdress]/JMP [Rel] thunks,
- // so we have to look into the code to see if the final target matches
- // Convert the FromProc into the thunk code it /might/ point to
- FromProcThunk := PWin95CallThunk(FromProc);
- // Is it a valid Win95 thunk?
- IsThunked := (Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and
- IsWin95CallThunk(FromProcThunk);
-
- // Get a pointer to the PE-header
- NtHeader := GetImageNtHeader(Base);
- // Get a pointer to the import descriptor table
- ImportDescriptor := PImageImportDescriptor(DWORD(Base)+
- NtHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
-
- // The table of import descriptors is marked with a null name offset
- while ImportDescriptor^.NameOffset <> 0 do
- begin
- // Calculate a pointer to this module name
- CurrModuleName := PChar(Base) + ImportDescriptor^.NameOffset;
- // Only search matching modules
- if StrIComp(CurrModuleName, ModuleName) = 0 then
- begin
-
- // Calculate a pointer to the first import entry for this module
- ImportEntry := PImageThunkData(DWORD(Base) + ImportDescriptor^.IATOffset);
-
- // Loop until we have reached the end of the list
- while ImportEntry^.FunctionPtr <> nil do
- begin
- // Now we must determine if this import entry pointer
- // is equivalent to the FromProc address
-
- if IsThunked then
- begin
- // Convert the ImportEntry into the thunk code it /might/ point to
- ImportThunk := PWin95CallThunk(ImportEntry^.FunctionPtr);
-
- // If the routine we're hooking points to a Win95 thunk,
- // see if the final target matches
- FoundProc := IsWin95CallThunk(ImportThunk) and
- (ImportThunk^.Addr = FromProcThunk^.Addr)
- end
- else
- // otherwise, only check for clean matches
- FoundProc := (ImportEntry^.FunctionPtr = FromProc);
-
- // If we found the correct import entry, patch it!
- if FoundProc then
- begin
- // Note that all import sections have Read/Write access by default,
- // so there is no need to play around with WriteProcessMemory or
- // VirtualProtect
- ImportEntry^.FunctionPtr := ToProc;
-
- // There could be more imports of the same routine, so just flag
- // success and keep looking for more matches
- Result := true;
- end;
-
- // Look at the next Import Entry for this module
- Inc(ImportEntry);
- end;
- end;
- // Look at the next Import Descriptor
- Inc(ImportDescriptor);
- end;
- end;
-
- function HookImport(ModuleName, ImportName: PChar; HookProc: pointer; var DLLProc: pointer): boolean;
- begin
- Result := not Assigned(DLLProc);
- if Result then
- begin
- DllProc := Windows.GetProcAddress(Windows.GetModuleHandle(ModuleName), ImportName);
- Result := Assigned(DllProc) and
- ReplaceImport(Pointer(HInstance), ModuleName, DllProc, HookProc);
- if not Result then
- DLLProc := nil;
- end;
- end;
-
- function UnhookImport(ModuleName, ImportName: PChar; HookProc: pointer; var DLLProc: pointer): boolean;
- begin
- Result := Assigned(DllProc) and
- ReplaceImport(Pointer(HInstance), ModuleName, HookProc, DllProc);
- if Result then
- DLLProc := nil;
- end;
-
- end.
-
-